#include "commonapi.h"
#include "driver/uart.h"

#include "esp_log.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/ringbuf.h"
#include "soc/lldesc.h"

#include "time.h"

#include "gpio_init.h"
#include "string.h"
#include "profile.h"
#include "gSensor.h"
#include "hx71x.h"
#include "protocal.h"
#include "psm.h"
#include "server.h"
#include "nbiot.h"
#include "fsm.h"

#include "nmea_parser.h"

static const char* TAG = "nbiot";

#define GPS_TEST 0

#define NBIOT_TASK_PERIDIC 20

#define STACK_SIZE_NBLIOT 4096
#define NB_RX_BUFF_SIZE 1024
#define NB_TX_BUFF_SIZE 1024
#define NB_APP_RX_BUF_SIZE (1024+32)

#define RX_STATUS_CMD 0
#define RX_STATUS_END 1
#define RX_STATUS_DATA 2

#define CONNECTION_STATUS_DISCONNECT 0
#define CONNECTION_STATUS_CONNECTING 1
#define CONNECTION_STATUS_CONNECTED 2
#define CONNECTION_STAUS_DISCONNECTING 3

#define DATA_LEN 32

#define NB_TXD_PIN (17)
#define NB_RXD_PIN (13)
#define NB_PORT UART_NUM_1
// login status

#define TIME_ZONE (+8)   //Beijing Time
#define YEAR_BASE (2000) //date in GPS starts from 2000

#define DATA_SEND_TRY_MAX 10
#define DATA_RESEND_TIMEOUT 300030

#define MAX_CMD_CATCH 32

#define SET_NBIOT_STATUS(status) nbiot_status = status
typedef void (*nbiot_ack_handle)(char*);
typedef void (*noiot_cmd_prehand)(char*);

typedef struct
{
    const char* cmd;
    noiot_cmd_prehand prehandle;
    nbiot_ack_handle handle;
    uint32_t timeout;
    uint8_t try_cnt;
} nbiot_cmd_tbl_t;

//fsm handler
static uint8_t nbiot_start_action(uint16_t msg);

//fsm current status handler
static uint8_t nbiot_start_action(uint16_t msg);

static uint8_t nbiot_cs_root(void);
static uint8_t noiot_cs_poweron(void);
static uint8_t nbiot_cs_ready(void);
static uint8_t nbiot_cs_runing(void);
static uint8_t nbiot_cs_shutdwon(void);
static uint8_t nbiot_cs_poweroff(void);
//fsm transfer handler
static uint8_t nbiot_trans_reboot(uint16_t msg);

//fsm entry handler
static uint8_t nbiot_entry_root(uint16_t msg);
static uint8_t nbiot_entry_poweron(uint16_t msg);
static uint8_t nbiot_entry_ready(uint16_t msg);
static uint8_t nbiot_entry_run(uint16_t msg);
static uint8_t nbiot_entry_shutdown(uint16_t msg);
static uint8_t nbiot_entry_poweroff(uint16_t msg);

static void common_handler(char*);
static void at_handle(char*);
static void qcgedefcont_handler(char*);
static void cimi_handle(char*);
static void iccid_handle(char*);
static void cclk_handler(char*);
static void csq_handle(char*);
static void qiopen_handle(char*);
static void qisend_handle(char*);
static void qiclose_handle(char*);
static void qiopen_prehandle(char*);
static void qisend_prehandle(char*);
static void qposd_handle(char*);
typedef enum
{
    nbiot_at,
    nbiot_ate0,
    nbiot_qicfg,
    nbiot_qgnssc,
    nbiot_qgsnssagps,
    nbiot_cpsms,
    nbiot_cgdcnt,
    nbiot_qcgdefcont,
    nbiot_cgdcntcheck,
    nbiot_cedrxs,
    nbiot_cimi,
    noiot_qiccid,
    nbiot_qband,
    nbiot_cclk,
    nbiot_csq,
    nbiot_cesq,
    nbiot_nbiotevent,
    nbiot_cerge5,
    nbiot_qiclose,
    nbiot_qiopen,
    nbiot_qisend,
    nbiot_qpowd,
    nbiot_cmd_cnt,
} cmd_step_t;

TaskHandle_t xHandle_nbiot = NULL;

const nbiot_cmd_tbl_t nbiot_cmd_tbl[] = {
    {"AT\r\n", NULL, at_handle, 1000, 20},
    {"ATE0\r\n", NULL, common_handler, 1000, 3},
    {"AT+QICFG=\"echomode\",0\r\n", NULL, common_handler, 1000, 3},
    {"AT+QGNSSC=1\r\n", NULL, common_handler, 1000, 3},
    {"AT+QGNSSAGPS=1\r\n", NULL, common_handler, 1000, 3},
    {"AT+CPSMS=0\r\n", NULL, common_handler, 1000, 3},
    {"AT+CGDCONT=1,\"IP\",\"ue.preter.ctnb\"\r\n", NULL, common_handler, 1000, 3},
    {"AT+QCGDEFCONT=\"IP\",\"ue.preter.ctnb\"\r\n", NULL, qcgedefcont_handler, 1000, 3},
    {"AT+CGDCONT?\r\n", NULL, common_handler, 1000, 3},
    {"AT+CEDRXS=0\r\n", NULL, common_handler, 1000, 3},
    {"AT+CIMI\r\n", NULL, cimi_handle, 1000, 3},
    {"AT+QCCID\r\n", NULL, iccid_handle, 300, 3},
    {"AT+QBAND=1,5\r\n", NULL, common_handler, 100, 3},
    {"AT+CCLK?\r\n", NULL, cclk_handler, 100, 3},
    {"AT+CSQ\r\n", NULL, csq_handle, 1000, 3},
    {"AT+CESQ\r\n", NULL, common_handler, 1000, 3},
    {"AT+QNBIOTEVENT=1,1\r\n", NULL, common_handler, 1000, 3},
    {"AT+CEREG=5\r\n", NULL, common_handler, 1000, 3},
    {"AT+QICLOSE=0\r\n", NULL, qiclose_handle, 1000, 3},
    {NULL, qiopen_prehandle, qiopen_handle, 300000, 3},
    {NULL, qisend_prehandle, qisend_handle, 300000, 3},
    {"AT+QPOWD=0\r\n", NULL, qposd_handle, 1000, 3},
};
nmea_parser_handle_t nmea_hdl;
static QueueHandle_t xQueue;

static uint8_t rx_status;
static uint8_t current_status;
static TickType_t current_status_timer;

static TickType_t cmd_send_timeout;
static uint8_t cmd_try_cnt;
static uint8_t cmd_step = 0xFF;
static uint8_t cmd_step_tbl[MAX_CMD_CATCH];
static uint8_t cmd_step_head;
static uint8_t cmd_step_tail;
static uint8_t cmd_cnt;
static uint8_t nb_rx_buff[NB_APP_RX_BUF_SIZE];
static uint16_t nb_rx_idx;
static uint8_t rx_data[DATA_MAX_LEN];
static uint8_t rx_data_len;
static uint8_t rx_data_idx;

static msg_t data_send_buf;
static uint8_t data_send_try_cnt;
static TickType_t data_send_timeout;
// static TickType_t data_send_breath_timer;
static bool is_data_sending;

static StaticTask_t xTaskBuffer_nbliot;
static StackType_t xStack_nbliot[STACK_SIZE_NBLIOT];

static uint8_t nbiot_imsi[8];
static uint8_t nbiot_iccid[10];
static uint8_t current_csq;
static bool default_pdn_set;
static bool is_time_sync;
// static bool nbiot_is_register;
// static uint8_t connection_status;

/********************************/
/* STATE TRANSITION DESCRIPTION */
/********************************/
#include "fsm_tran.h"
#include "nbiot_stt.h"

/********************************/
/* STATE TREE DESCRIPTION       */
/********************************/
#include "fsm_tree.h"
#include "nbiot_stt.h"

static void uart_init(void)
{
    const uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_APB,
    };
    uart_driver_install(NB_PORT, NB_RX_BUFF_SIZE, NB_TX_BUFF_SIZE, 0, NULL, 0);
    uart_param_config(NB_PORT, &uart_config);
    uart_set_pin(NB_PORT, NB_TXD_PIN, NB_RXD_PIN, UART_PIN_NO_CHANGE,
        UART_PIN_NO_CHANGE);
}

static bool nbiot_add_cmd(cmd_step_t cmd)
{
    if (cmd_cnt >= MAX_CMD_CATCH)
    {
        ESP_LOGE("NB", "add cmd fail");
        return false;
    }
    cmd_step_tbl[cmd_step_tail++] = (uint8_t)cmd;
    if (cmd_step_tail == MAX_CMD_CATCH)
    {
        cmd_step_tail = 0;
    }
    cmd_cnt++;
    return true;
}

static uint8_t nbiot_get_cmd(void)
{
    if (cmd_cnt != 0)
    {
        return cmd_step_tbl[cmd_step_head];
    }
    return 0xff;
}

static void nbiot_del_cmd(void)
{
    if (cmd_cnt != 0)
    {
        cmd_step_head++;
        if (cmd_step_head == MAX_CMD_CATCH)
        {
            cmd_step_head = 0;
        }
        cmd_cnt--;
    }
    cmd_try_cnt = 0;
}

static void gps_event_handler(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
    // static bool timesync;
    gps_t* gps = NULL;
    switch (event_id)
    {
    case GPS_UPDATE:
        gps = (gps_t*)event_data;
        // ESP_LOGI(TAG, "gps date: %d-%d-%d\n", gps->date.year, gps->date.month, gps->date.day);
        // if (/*!timesync && */gps->valid)
        // {
        //     ESP_LOGI(TAG, "set time\n");
        //     struct tm* ptime;
        //     time_t rawtime;
        //     time(&rawtime);
        //     ptime = localtime(&rawtime);
        //     if (ptime != 0)
        //     {
        //         ptime->tm_sec = gps->tim.second;
        //         ptime->tm_min = gps->tim.minute;
        //         ptime->tm_hour = gps->tim.hour;
        //         ptime->tm_mday = gps->date.day;
        //         ptime->tm_mon = gps->date.month - 1;
        //         ptime->tm_year = gps->date.year + 2000 - 1970;
        //         mktime(ptime);
        //         //timesync = true;
        //     }
        // }
        location_t location_info = { 0 };
        location_info.fix = gps->fix;
        location_info.altitude = gps->altitude;
        location_info.latitude = gps->latitude;
        location_info.longitude = gps->longitude;
        protocal_location_update(&location_info);

        break;
    case GPS_UNKNOWN:
        /* print unknown statements */
        break;
    default:
        break;
    }
}

static void nmea_init(void)
{
    nmea_parser_config_t config = NMEA_PARSER_CONFIG_DEFAULT();
    nmea_hdl = nmea_parser_init(&config);
    nmea_parser_add_handler(nmea_hdl, gps_event_handler, NULL);
}

static void nmea_deinit(void)
{
    if (nmea_hdl)
    {
        nmea_parser_deinit(nmea_hdl);
        nmea_parser_remove_handler(nmea_hdl, gps_event_handler);
        nmea_hdl = NULL;
    }
}

static bool urc_handle(const char* urc)
{
    if (strncmp(urc, "+QIURC: \"recv\",0,", 17) == 0)
    {
        sscanf(&urc[17], "%hhd", &rx_data_len);
        rx_status = RX_STATUS_DATA;
        return true;
    }
    else if (strncmp(urc, "+IP:", 4) == 0)
    {
        nbiot_send_evt(NBIOT_EVT_RUN, 0);
        nbiot_add_cmd(nbiot_qgsnssagps);
        nbiot_add_cmd(nbiot_cpsms);
        nbiot_add_cmd(nbiot_cedrxs);

        // nbiot_is_register = true;
        return true;
    }
    else if (strncmp(urc, "+CPIN: READY", 12) == 0)
    {
        // nbiot_add_cmd(nbiot_csq);
        nbiot_add_cmd(noiot_qiccid);
        nbiot_send_evt(NBIOT_EVT_REDY, 0);
    }
    else if (strncmp(urc, "+QIURC: \"closed\",", 17) == 0)
    {
        // connection_status = CONNECTION_STATUS_DISCONNECT;
    }
    return false;
}

static void nbiot_rx_parse_data(uint8_t data)
{
    rx_data[rx_data_idx++] = data;
    if (rx_data_idx == rx_data_len)
    {
        ESP_LOGI(TAG, "rx: ");
        printhex(rx_data, rx_data_len);
        protocal_rx_handle(rx_data, rx_data_len);
        rx_data_idx = 0;
        rx_data_len = 0;
        memset(rx_data, 0, DATA_MAX_LEN);
    }
}

static void nbiot_rx(void)
{
    uint8_t tmp = 0;
    uint16_t rxBytes = uart_read_bytes(UART_NUM_1, &tmp, 1, 0);
    while (rxBytes)
    {
        switch (rx_status)
        {
        case RX_STATUS_CMD:
            nb_rx_buff[nb_rx_idx] = tmp;
            nb_rx_idx++;
            if (tmp == '\r')
            {
                rx_status = 1;
            }
            break;
        case RX_STATUS_END:
            nb_rx_buff[nb_rx_idx] = tmp;
            if (tmp == '\n')
            {
                rx_status = 0;
                nb_rx_buff[nb_rx_idx + 1] = 0;
                if (nb_rx_buff[0] != '\r')
                {
                    printf("%s", nb_rx_buff);
                }
                if (!urc_handle((const char*)nb_rx_buff))
                {
                    if (cmd_step < nbiot_cmd_cnt && nbiot_cmd_tbl[cmd_step].handle)
                    {
                        nbiot_cmd_tbl[cmd_step].handle((char*)nb_rx_buff);
                    }
                }
                nb_rx_idx = 0;
            }
            break;
        case RX_STATUS_DATA:
            nbiot_rx_parse_data(tmp);
            break;
        default:
            break;
        }
        rxBytes = uart_read_bytes(UART_NUM_1, &tmp, 1, 0);
    }
}

static void nbiot_cmd_send(void)
{
    cmd_step = nbiot_get_cmd();
    if (cmd_step >= nbiot_cmd_cnt)
    {
        return;
    }
    if (cmd_try_cnt >= nbiot_cmd_tbl[cmd_step].try_cnt)
    {
        ESP_LOGW(TAG, "sent tryout\n");
        nbiot_send_evt(NBIOT_EVT_REBOOT, 0);
        cmd_step = 0xFF;
        return;
        //todo: reboot
    }
    else
    {
        if (xTaskGetTickCount() > cmd_send_timeout)
        {
            const char* cmd;
            char cmd_buf[128];
            cmd_send_timeout = xTaskGetTickCount() +
                nbiot_cmd_tbl[cmd_step].timeout / portTICK_PERIOD_MS;
            if (nbiot_cmd_tbl[cmd_step].prehandle)
            {
                memset(cmd_buf, 0, 128);
                nbiot_cmd_tbl[cmd_step].prehandle(cmd_buf);
                cmd = (const char*)cmd_buf;
            }
            else
            {
                cmd = (const char*)nbiot_cmd_tbl[cmd_step].cmd;
            }

            //ESP_LOGI(TAG, "tx cmd: %s\r\n", cmd);
            cmd_try_cnt++;
            uart_write_bytes(UART_NUM_1, cmd, strlen(cmd));
        }
    }
}

static void common_handler(char* ack)
{
    if (strncmp(ack, "OK", 2) == 0)
    {
        cmd_send_timeout = 0;
        nbiot_del_cmd();
    }
}

static void at_handle(char* ack)
{
    if (strncmp(ack, "OK", 2) == 0)
    {
        cmd_send_timeout = 0;
        nbiot_del_cmd();
    }
}

static void qcgedefcont_handler(char* ack)
{
    if (strncmp(ack, "OK", 2) == 0)
    {
        //cmd_send_timeout = 0;
        nbiot_del_cmd();
        nbiot_send_evt(NBIOT_EVT_REBOOT, 0);
        default_pdn_set = true;
    }
}

static void cimi_handle(char* ack)
{
    uint8_t tmp_imsi[16] = "";
    if (strncmp(ack, "OK", 2) == 0)
    {
        cmd_send_timeout = 0;
        nbiot_del_cmd();
    }
    else if (strncmp(ack, "46", 2) == 0)
    {
        uint8_t i;
        memcpy(tmp_imsi, ack, 16);
        for (i = 0; i < 7; i++)
        {
            nbiot_imsi[i] = (tmp_imsi[i * 2] - '0') << 4;
            nbiot_imsi[i] |= (tmp_imsi[i * 2 + 1] - '0');
        }
        nbiot_imsi[i] = (tmp_imsi[i * 2] - '0') << 4;
    }
}

static void iccid_handle(char* ack)
{
    uint8_t tmp[20] = "";
    if (strncmp(ack, "OK", 2) == 0)
    {
        cmd_send_timeout = 0;
        nbiot_del_cmd();
    }
    else if (strncmp(ack, "+QCCID: ", 8) == 0)
    {
        uint8_t i;
        memcpy(tmp, &ack[8], 20);
        for (i = 0; i < 10; i++)
        {
            nbiot_iccid[i] = (tmp[i * 2] - '0') << 4;
            nbiot_iccid[i] |= (tmp[i * 2 + 1] - '0');
        }
    }
}

static void cclk_handler(char* ack)
{
    uint16_t  year = 0;
    uint8_t mouth = 0, day = 0, hour = 0, min = 0, sec = 0;
    int8_t timezone = 0;
    if (strncmp(ack, "+CCLK: ", 7) == 0)
    {
        sscanf(&ack[7], "%hd/%hhd/%hhd,%hhd:%hhd:%hhd", &year, &mouth, &day, &hour, &min, &sec);
        if (year >= 2020) {

            //ESP_LOGI(TAG, "set time: %hd/%hhd/%hhd,%hhd:%hhd:%hhd\n", year, mouth, day, hour, min, timezone);
            struct tm* ptime;
            time_t rawtime;
            struct timeval tv;
            time_t timep;
            time(&rawtime);
            ptime = localtime(&rawtime);
            if (ptime != 0)
            {
                is_time_sync = true;
                ptime->tm_sec = sec;
                ptime->tm_min = min;
                ptime->tm_hour = hour;
                ptime->tm_mday = day;
                ptime->tm_mon = mouth - 1;
                ptime->tm_year = year - 1900;
                timep = mktime(ptime);
                tv.tv_sec = timep + 28800;
                tv.tv_usec = 0;
                timezone = 8;
                if (settimeofday(&tv, (struct timezone*)0) < 0)
                {
                    ESP_LOGE(TAG, "set time error\n");
                }
                //timesync = true;
            }
        }
    }
    else if (strncmp(ack, "OK", 2) == 0)
    {
        cmd_send_timeout = 0;
        nbiot_del_cmd();
    }
}

static void csq_handle(char* ack)
{
    if (strncmp(ack, "OK", 2) == 0)
    {
        cmd_send_timeout = 0;
        nbiot_del_cmd();
    }
    else if (strncmp(ack, "+CSQ: ", 6) == 0)
    {
        int csq = atoi(&ack[6]);
        current_csq = (uint8_t)csq;
        // printf("current_csq:%hhd\n", current_csq);
    }
}

static void qiopen_handle(char* ack)
{
    if (strncmp(ack, "+QIOPEN: 0,", 11) == 0)
    {
        int err_code = atol(&ack[11]);
        if (err_code == 0)
        {
            // connection_status = CONNECTION_STATUS_CONNECTED;
            nbiot_del_cmd();
        }
        cmd_send_timeout = 0;
    }
    else if (strncmp(ack, "ERROR", 5) == 0)
    {
        // connection_status = CONNECTION_STATUS_CONNECTED;
        cmd_send_timeout = 0;
        nbiot_del_cmd();
    }
}

static void qisend_handle(char* ack)
{
    if (strncmp(ack, ">", 1) == 0)
    {
        ESP_LOGI(TAG, "tx: ");
        printhex(data_send_buf.data, data_send_buf.len);
        uart_write_bytes(UART_NUM_1, (const char*)data_send_buf.data,
            data_send_buf.len);
    }
    else if (strncmp(ack, "SEND OK", 7) == 0)
    {
        nbiot_del_cmd();
        is_data_sending = false;
        data_send_timeout = 0;
        rx_status = RX_STATUS_CMD;
        memset(&data_send_buf, 0, sizeof(msg_t));
        cmd_send_timeout = 0;
    }
    else if (strncmp(ack, "SEND FAIL", 9) == 0)
    {
        data_send_timeout = 0;
        rx_status = RX_STATUS_CMD;
        cmd_send_timeout = 0;
    }
}

static void qiclose_handle(char* ack)
{

    if (strncmp(ack, "CLOSE OK", 8) == 0)
    {
        // connection_status = CONNECTION_STATUS_DISCONNECT;
        cmd_send_timeout = 0;
        nbiot_del_cmd();
    }
    else if (strncmp(ack, "ERROR", 5) == 0)
    {
        // connection_status = CONNECTION_STATUS_DISCONNECT;
        cmd_send_timeout = 0;
        nbiot_del_cmd();
    }
}

static void qposd_handle(char* ack)
{
    if (strncmp(ack, "OK", 2) == 0)
    {
        cmd_send_timeout = 0;
        nbiot_del_cmd();
        nbiot_send_evt(NBIOT_EVT_POWEROFF, 0);
    }
}

static void qiopen_prehandle(char* buf)
{
    sprintf(buf, "AT+QIOPEN=1,0,\"TCP\",%s,%d,0,1\r\n", runtime_profile->Struct.addr, runtime_profile->Struct.port);
}

static void qisend_prehandle(char* buf)
{
    sprintf(buf, "AT+QISEND=0,%d\r\n", data_send_buf.len);
}

static uint8_t nbiot_start_action(uint16_t msg)
{
    ESP_LOGI(TAG, "start\n");
    return 0;
}

static uint8_t nbiot_cs_root(void)
{
    nbiot_cmd_send();
    nbiot_rx();
    return 0;
}

static uint8_t noiot_cs_poweron(void)
{
    if (current_status_timer + pdMS_TO_TICKS(60000) < xTaskGetTickCount())
    {
        ESP_LOGW(TAG, "poweron timeout\n");
        nbiot_send_evt(NBIOT_EVT_REBOOT, 0);
    }
    return 0;
}

static uint8_t nbiot_cs_ready(void)
{
    if (current_status_timer + pdMS_TO_TICKS(60000) < xTaskGetTickCount())
    {
        ESP_LOGW(TAG, "wait ready timeout\n");
        nbiot_send_evt(NBIOT_EVT_REBOOT, 0);
    }
    return 0;
}

static uint8_t nbiot_cs_runing(void)
{
    TickType_t cur_time = xTaskGetTickCount();
    protocal_collect_schedule();
    if ((psm_get_status() == PM_CHRAGING || psm_get_status() == PM_GOTO_SLEEP) && !protocal_is_has_data() && protcal_is_logout() && cmd_cnt == 0 && !is_data_sending && tcp_all_client_close())
    {
        nbiot_send_evt(NBIOT_EVT_SHUTDOWN, 0);
    }
    else if (is_data_sending)
    {
        if (data_send_try_cnt <= DATA_SEND_TRY_MAX)
        {
            if (data_send_timeout <= cur_time)
            {
                data_send_timeout = cur_time + DATA_RESEND_TIMEOUT / portTICK_PERIOD_MS;
                ESP_LOGI(TAG, "Resend\n");
                nbiot_add_cmd(nbiot_csq);
                nbiot_add_cmd(nbiot_qiopen);
                nbiot_add_cmd(nbiot_qisend);
                nbiot_add_cmd(nbiot_qiclose);
                data_send_try_cnt++;
            }
        }
        else
        {
            is_data_sending = false;
        }
    }
    else
    {
        if (protocal_queue_get_data(&data_send_buf))
        {
            data_send_try_cnt = 0;
            is_data_sending = true;
            data_send_timeout = cur_time + DATA_RESEND_TIMEOUT / portTICK_PERIOD_MS;
            if (!is_time_sync)
            {
                nbiot_add_cmd(nbiot_cclk);
            }
            nbiot_add_cmd(nbiot_csq);
            nbiot_add_cmd(nbiot_qiopen);
            nbiot_add_cmd(nbiot_qisend);
            nbiot_add_cmd(nbiot_qiclose);
            data_send_try_cnt++;
        }
    }
    return 0;
}

static uint8_t nbiot_cs_shutdwon(void)
{
    if (current_status_timer + pdMS_TO_TICKS(60000) < xTaskGetTickCount())
    {
        nbiot_send_evt(NBIOT_EVT_POWEROFF, 0);
    }
    return 0;
}

static uint8_t nbiot_cs_poweroff(void)
{

    if (current_status != 0xFF)
    {
        if (current_status_timer + pdMS_TO_TICKS(1000) < xTaskGetTickCount())
        {
            ESP_LOGI(TAG, "poweroff\n");
            gpio_set_level(GPIO_NBIOT_EN, 0);
            current_status = 0xFF;
            vTaskSuspend(NULL);
        }
    }
    if (psm_get_status() == PM_RUN)
    {
        nbiot_send_evt(NBIOT_EVT_START, 0);
    }
    return 0;
}

static uint8_t nbiot_trans_reboot(uint16_t msg)
{
    ESP_LOGI(TAG, "rebooting\n");
    gpio_set_level(GPIO_NBIOT_EN, 0);
    nmea_deinit();
    vTaskDelay(500 / portTICK_PERIOD_MS);
    // current_status_timer = xTaskGetTickCount();
    return NBIOT_POWERON;
}

static uint8_t nbiot_entry_root(uint16_t msg)
{
    ESP_LOGI(TAG, "entry root\n");
    if (local_profile.Struct.is_avilid)
    {
        runtime_profile = &local_profile;
    }
    else
    {
        ESP_LOGW(TAG, "use default profile\n");
    }

    nbiot_send_evt(NBIOT_EVT_START, 0);
    return NBIOT_ROOT;
}

static void nbiot_clear_cmd(void)
{
    cmd_send_timeout = 0;
    cmd_try_cnt = 0;
    cmd_step = 0xff;
    memset(cmd_step_tbl, 0, sizeof(cmd_step_tbl));
    cmd_step_head = 0;
    cmd_step_tail = 0;
    cmd_cnt = 0;
    nb_rx_idx = 0;
    rx_data_len = 0;
    rx_data_idx = 0;
    data_send_timeout = 0;
    // is_data_sending = false;
}

static uint8_t nbiot_entry_poweron(uint16_t msg)
{
    nbiot_clear_cmd();
    gpio_set_level(GPIO_NBIOT_EN, 1);
    vTaskDelay(100 / portTICK_PERIOD_MS);
    gpio_set_level(GPIO_NBIOT_PWK, 1);
    vTaskDelay(510 / portTICK_PERIOD_MS);
    gpio_set_level(GPIO_NBIOT_PWK, 0);
    nbiot_add_cmd(nbiot_at);
    // nbiot_add_cmd(nbiot_ate0);
    nbiot_add_cmd(nbiot_qgnssc);
    nbiot_add_cmd(nbiot_qicfg);
    // if (!default_pdn_set)
    // {
    //     nbiot_add_cmd(nbiot_qcgdefcont);
    // }
    nbiot_add_cmd(nbiot_cgdcnt);
    // nbiot_add_cmd(nbiot_cgdcntcheck);
    // else
    // {
    nbiot_add_cmd(nbiot_nbiotevent);
    nbiot_add_cmd(nbiot_cerge5);
    // }
    current_status_timer = xTaskGetTickCount();
    return NBIOT_POWERON;
}

static uint8_t nbiot_entry_ready(uint16_t msg)
{
    ESP_LOGI(TAG, "entry ready\n");
    nmea_init();
    current_status_timer = xTaskGetTickCount();
    return NBIOT_REDY;
}

static uint8_t nbiot_entry_run(uint16_t msg)
{
    ESP_LOGI(TAG, "entry running\n");
    return NBIOT_RUNING;
}
static uint8_t nbiot_entry_shutdown(uint16_t msg)
{
    ESP_LOGI(TAG, "entry shutdown\n");
    nbiot_add_cmd(nbiot_qpowd);
    current_status_timer = xTaskGetTickCount();
    return NBIOT_SHUTDOWN;
}

static uint8_t nbiot_entry_poweroff(uint16_t msg)
{
    ESP_LOGI(TAG, "entry poweroff\n");
    nmea_deinit();
    current_status_timer = xTaskGetTickCount();
    return NBIOT_POWEROFF;
}

static void nbliot_task(void* ppvParameter)
{
    (void)ppvParameter;
    Data_Message_T msg;
    BaseType_t status;
    msg.parts.msg = START;
    msg.parts.data = 0;

    uint8_t new_state = FSM_Process_Evt(msg, NBIOT_ROOT, tree_nbiot);
    // profile_load();
    for (;;)
    {

        // wait until next event in mailbox OR time expired
        status = xQueueReceive(xQueue, &msg, pdMS_TO_TICKS(20));
        // process ALL events in queue first !!!
        while (pdPASS == status)
        {
            // process event
            new_state = FSM_Process_Evt(msg, current_status, tree_nbiot);

            if (new_state != current_status)
            {
                current_status = new_state;
            }

            // get next event from mailbox without waiting
            status = xQueueReceive(xQueue, &msg, 0);
        }

        // process all cs routines for the active state tree branch
        FSM_Process_CS(current_status, tree_nbiot);
    }
}

void nbiot_task_init(void)
{
    uart_init();
    // nmea_init();
    xQueue = xQueueCreate(10, sizeof(Data_Message_T));
    if (xQueue == NULL)
    {
        ESP_LOGE(TAG, "Create nbiot queue failed\n");
        while (1)
            ;
    }
    // Create the task without using any dynamic memory allocation.
    xHandle_nbiot = xTaskCreateStatic(
        nbliot_task,          // Function that implements the task.
        "nbliot",             // Text name for the task.
        STACK_SIZE_NBLIOT,    // Stack size in bytes, not words.
        (void*)NULL,         // Parameter passed into the task.
        tskIDLE_PRIORITY + 1, // Priority at which the task is created.
        xStack_nbliot,        // Array to use as the task's stack.
        &xTaskBuffer_nbliot); // Variable to hold the task's data structure.
    if (!xHandle_nbiot)
    {
        ESP_LOGE(TAG, "Create nbiot task failed\n");
        while (1)
            ;
    }
}

bool nbiot_is_shutdown(void)
{
    return current_status == 0xff;
}

int8_t nbiot_get_sn(uint8_t* sn)
{
    if (sn == NULL)
    {
        return -1;
    }
    memcpy(sn, nbiot_iccid, 10);
    return 0;
}

uint8_t nbiot_get_csq(void)
{
    return current_csq;
}

uint8_t nbiot_send_evt(uint16_t event, int16_t data)
{
    Data_Message_T msg;
    uint8_t ret = 1;
    msg.parts.msg = event;
    msg.parts.data = data;
    if (pdPASS == xQueueSend(xQueue, &msg, 100))
    {
        ret = 0;
    }
    return ret;
}

void nbiot_reboot_req(void)
{
    nbiot_send_evt(NBIOT_EVT_REBOOT, 0);
}

void nbiot_suspend(void)
{
    gpio_set_level(GPIO_NBIOT_EN, 0);
    vTaskSuspend(xHandle_nbiot);
}